﻿using System;
using System.Collections.Generic;
using System.Security.Principal;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using BlogLite.Areas.Admin.Helpers;
using BlogLite.Areas.Admin.Models;
using BlogLite.Models.Database;
using BlogLite.Tests.Mocks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using System.Collections.Specialized;

namespace BlogLite.Tests.Controllers
{
    [TestClass]
    public class PostControllerTest : PostControllerTestContext
    {
        [TestMethod]
        public void Index()
        {
            // Act
            ViewResult result = controller.Index() as ViewResult;

            // Assert
            Assert.IsNotNull(result);
        }

        [TestMethod]
        public void GetGetPosts_Model_IsInstanceOfTypeJQGridData()
        {
            // Act
            JsonResult result = controller.GetPosts("", "", 1, 1) as JsonResult;

            // Assert
            Assert.IsInstanceOfType(result.Data, typeof(JQGridData));
        }

        [TestMethod]
        public void GetGetPosts_SortOrderIsNull_SortedByPostDateDesc()
        {
            // Arrange
            List<string> expectedEventNamesOrder = new List<string>()
            {
                "<a href='/Admin/Post/Edit/1'>Test Post 1</a>",
                "<a href='/Admin/Post/Edit/2'>Test Post 2</a>",
                "<a href='/Admin/Post/Edit/3'>Test Post 3</a>",
            };

            // Act
            JsonResult result = controller.GetPosts("PostDate", null, 1, 3) as JsonResult;

            // Assert
            JQGridData jsonResult = result.Data as JQGridData;
            Assert.AreEqual(expectedEventNamesOrder[0], jsonResult.rows[0].cell[0]);
            Assert.AreEqual(expectedEventNamesOrder[1], jsonResult.rows[1].cell[0]);
            Assert.AreEqual(expectedEventNamesOrder[2], jsonResult.rows[2].cell[0]);
        }

        [TestMethod]
        public void GetGetPosts_SortOrderIsAsc_SortedByPostDateAsc()
        {
            // Arrange
            List<string> expectedEventNamesOrder = new List<string>()
            {
                "<a href='/Admin/Post/Edit/3'>Test Post 3</a>",
                "<a href='/Admin/Post/Edit/2'>Test Post 2</a>",
                "<a href='/Admin/Post/Edit/1'>Test Post 1</a>",
            };

            // Act
            JsonResult result = controller.GetPosts("PostDate", "asc", 1, 3) as JsonResult;

            // Assert
            JQGridData jsonResult = result.Data as JQGridData;
            Assert.AreEqual(expectedEventNamesOrder[0], jsonResult.rows[0].cell[0]);
            Assert.AreEqual(expectedEventNamesOrder[1], jsonResult.rows[1].cell[0]);
            Assert.AreEqual(expectedEventNamesOrder[2], jsonResult.rows[2].cell[0]);
        }

        [TestMethod]
        public void GetGetPosts_SortOrderIsDesc_SortedByPostDateDesc()
        {
            // Arrange
            List<string> expectedEventNamesOrder = new List<string>()
            {
                "<a href='/Admin/Post/Edit/1'>Test Post 1</a>",
                "<a href='/Admin/Post/Edit/2'>Test Post 2</a>",
                "<a href='/Admin/Post/Edit/3'>Test Post 3</a>",
            };

            // Act
            JsonResult result = controller.GetPosts("PostDate", "desc", 1, 3) as JsonResult;

            // Assert
            JQGridData jsonResult = result.Data as JQGridData;
            Assert.AreEqual(expectedEventNamesOrder[0], jsonResult.rows[0].cell[0]);
            Assert.AreEqual(expectedEventNamesOrder[1], jsonResult.rows[1].cell[0]);
            Assert.AreEqual(expectedEventNamesOrder[2], jsonResult.rows[2].cell[0]);
        }

        [TestMethod]
        public void GetGetPosts_SortOrderIsAsc_SortedByTitleAsc()
        {
            // Arrange
            List<string> expectedEventNamesOrder = new List<string>()
            {
                "<a href='/Admin/Post/Edit/1'>Test Post 1</a>",
                "<a href='/Admin/Post/Edit/2'>Test Post 2</a>",
                "<a href='/Admin/Post/Edit/3'>Test Post 3</a>",
            };

            // Act
            JsonResult result = controller.GetPosts("Title", "asc", 1, 3) as JsonResult;

            // Assert
            JQGridData jsonResult = result.Data as JQGridData;
            Assert.AreEqual(expectedEventNamesOrder[0], jsonResult.rows[0].cell[0]);
            Assert.AreEqual(expectedEventNamesOrder[1], jsonResult.rows[1].cell[0]);
            Assert.AreEqual(expectedEventNamesOrder[2], jsonResult.rows[2].cell[0]);
        }

        [TestMethod]
        public void GetGetPosts_SortOrderIsDesc_SortedByPostTitleDesc()
        {
            // Arrange
            List<string> expectedEventNamesOrder = new List<string>()
            {
                "<a href='/Admin/Post/Edit/3'>Test Post 3</a>",
                "<a href='/Admin/Post/Edit/2'>Test Post 2</a>",
                "<a href='/Admin/Post/Edit/1'>Test Post 1</a>",
            };

            // Act
            JsonResult result = controller.GetPosts("Title", "desc", 1, 3) as JsonResult;

            // Assert
            JQGridData jsonResult = result.Data as JQGridData;
            Assert.AreEqual(expectedEventNamesOrder[0], jsonResult.rows[0].cell[0]);
            Assert.AreEqual(expectedEventNamesOrder[1], jsonResult.rows[1].cell[0]);
            Assert.AreEqual(expectedEventNamesOrder[2], jsonResult.rows[2].cell[0]);
        }

        [TestMethod]
        public void GetGetPosts_SortOrderIsAsc_SortedByLastModifiedAsc()
        {
            // Arrange
            List<string> expectedEventNamesOrder = new List<string>()
            {
                "<a href='/Admin/Post/Edit/3'>Test Post 3</a>",
                "<a href='/Admin/Post/Edit/2'>Test Post 2</a>",
                "<a href='/Admin/Post/Edit/1'>Test Post 1</a>",
            };

            // Act
            JsonResult result = controller.GetPosts("LastModified", "asc", 1, 3) as JsonResult;

            // Assert
            JQGridData jsonResult = result.Data as JQGridData;
            Assert.AreEqual(expectedEventNamesOrder[0], jsonResult.rows[0].cell[0]);
            Assert.AreEqual(expectedEventNamesOrder[1], jsonResult.rows[1].cell[0]);
            Assert.AreEqual(expectedEventNamesOrder[2], jsonResult.rows[2].cell[0]);
        }

        [TestMethod]
        public void GetGetPosts_SortOrderIsDesc_SortedByPostLastModifiedDesc()
        {
            // Arrange
            List<string> expectedEventNamesOrder = new List<string>()
            {
                "<a href='/Admin/Post/Edit/1'>Test Post 1</a>",
                "<a href='/Admin/Post/Edit/2'>Test Post 2</a>",
                "<a href='/Admin/Post/Edit/3'>Test Post 3</a>",
            };

            // Act
            JsonResult result = controller.GetPosts("LastModified", "desc", 1, 3) as JsonResult;

            // Assert
            JQGridData jsonResult = result.Data as JQGridData;
            Assert.AreEqual(expectedEventNamesOrder[0], jsonResult.rows[0].cell[0]);
            Assert.AreEqual(expectedEventNamesOrder[1], jsonResult.rows[1].cell[0]);
            Assert.AreEqual(expectedEventNamesOrder[2], jsonResult.rows[2].cell[0]);
        }

        [TestMethod]
        public void GetCreate_ViewModel_IsInstanceOfTyepPostViewModel()
        {
            // Act
            ViewResult result = controller.Create() as ViewResult;

            // Assert
            Assert.IsInstanceOfType(result.Model, typeof(PostViewModel));
        }

        [TestMethod]
        public void PostCreate_ModelIsNotValid_ReturnsCurrentViewModel()
        {
            // Arrange
            var postDate = DateTime.Now;
            
            controller.ModelState.AddModelError("Test Error", new Exception("Test Error"));

            // Act
            ViewResult result = controller.Create(new Post() { Subject = "Test Post", Content = "<p>Test Post</p>", PostDate = postDate }, new List<string>(), "") as ViewResult;

            // Assert     
            var viewData = result.Model as PostViewModel;
            Assert.IsTrue(viewData.Post.Subject == "Test Post");
            Assert.IsTrue(viewData.Post.Content == "<p>Test Post</p>");
            Assert.IsTrue(viewData.Post.PostDate == postDate);
        }

        [TestMethod]
        public void PostCreate_ModelIsValid_PostIsSaved()
        {
            // Arrange           
            Mock<HttpContextBase> context = new Mock<HttpContextBase>();
            IPrincipal user = new GenericPrincipal(new GenericIdentity("Lancer"), null);
            context.Setup(u => u.User).Returns(user);
            controller.ControllerContext = new ControllerContext(context.Object, new RouteData(), controller);
            
            Post post = new Post() { Subject = "Test Post", Content = "<p>Test Post</p>", PostDate = DateTime.Now };

            // Act
            RedirectResult result = controller.Create(post, new List<string>(), "") as RedirectResult;

            // Assert
            repository.Verify(r => r.Save(), Times.Once(), "Verify Failed");
        }

        [TestMethod]
        public void PostCreate_PostSaved_RedirectedToIndex()
        {
            // Arrange
            Mock<HttpContextBase> context = new Mock<HttpContextBase>();
            IPrincipal user = new GenericPrincipal(new GenericIdentity("Lancer"), null);
            context.Setup(u => u.User).Returns(user);
            controller.ControllerContext = new ControllerContext(context.Object, new RouteData(), controller);

            Post post = new Post() { Subject = "Test Post", Content = "<p>Test Post</p>", PostDate = DateTime.Now };            

            // Act
            RedirectToRouteResult result = controller.Create(post, new List<string>(), "") as RedirectToRouteResult;

            // Assert
            Assert.AreEqual("Index", result.RouteValues["action"]);
        }

        [TestMethod]
        public void GetEdit_PostIsNull_RedirectsToError()
        {
            // Act
            RedirectToRouteResult result = controller.Edit(404) as RedirectToRouteResult;

            // Assert
            Assert.AreEqual("Admin_Error404", result.RouteName);
        }

        [TestMethod]
        public void GetEdit_ViewModel_IsInstanceOfTypePostViewModel()
        {
            // Act
            ViewResult result = controller.Edit(1) as ViewResult;

            // Assert
            Assert.IsInstanceOfType(result.ViewData.Model, typeof(PostViewModel));
        }

        [TestMethod]
        public void GetEdit_ViewModel_PostIsNotNull()
        {
            // Act
            ViewResult result = controller.Edit(1) as ViewResult;

            // Assert
            var viewModel = result.ViewData.Model as PostViewModel;
            Assert.IsNotNull(viewModel.Post);
        }

        [TestMethod]
        public void GetEdit_SelectedTags_CountGreaterOrEqualThanZero()
        {
            // Act
            ViewResult result = controller.Edit(1) as ViewResult;

            // Assert
            var viewModel = result.ViewData.Model as PostViewModel;
            Assert.IsTrue(viewModel.SelectedTags.Count >= 0);
        }

        [TestMethod]
        public void PostEdit_ModelStateIsNotValid_ReturnsCurrentViewModel()
        {
            var postDate = DateTime.Now;
            
            controller.ModelState.AddModelError("Test Error", new Exception("Test Error"));

            // Act
            ViewResult result = controller.Create(new Post() { Subject = "Test Post", Content = "<p>Test Post</p>", PostDate = postDate }, new List<string>(), "") as ViewResult;

            // Assert     
            var viewData = result.Model as PostViewModel;
            Assert.IsTrue(viewData.Post.Subject == "Test Post");
            Assert.IsTrue(viewData.Post.Content == "<p>Test Post</p>");
            Assert.IsTrue(viewData.Post.PostDate == postDate);
        }

        [TestMethod]
        public void PostEdit_ModelIsValid_PostIsSaved()
        {
            // Arrange           
            Mock<HttpContextBase> context = new Mock<HttpContextBase>();          
            controller.ControllerContext = new ControllerContext(context.Object, new RouteData(), controller);

            // Setup the valueprovider so UpdateModel doesn't have a cow
            controller.ValueProvider = new FormCollection().ToValueProvider();

            Post post = new Post() { PostId = 1, Subject = "Test Post", Content = "<p>Test Post</p>",  PostDate = DateTime.Now };           

            // Act
            RedirectResult result = controller.Edit(post, new List<string>(), "") as RedirectResult;

            // Assert
            repository.Verify(r => r.Save(), Times.Once(), "Verify Failed");
        }

        [TestMethod]
        public void PostEdit_PostSaved_RedirectedToIndex()
        {
            // Arrange
            Mock<HttpContextBase> context = new Mock<HttpContextBase>();

            // Setup the valueprovider so UpdateModel doesn't have a cow
            controller.ValueProvider = new FormCollection().ToValueProvider();

            controller.ControllerContext = new ControllerContext(context.Object, new RouteData(), controller);

            Post post = new Post() { PostId = 1, Subject = "Test Post", Content = "<p>Test Post</p>", PostDate = DateTime.Now };

            // Act
            RedirectToRouteResult result = controller.Edit(post, new List<string>(), "") as RedirectToRouteResult;

            // Assert
            Assert.AreEqual("Index", result.RouteValues["action"]);
        }
    }
}